﻿using EasyDDD.Domain;

namespace EasyDDD.Infrastructure.EFCore.Repository
{
    public abstract class Repository<TContext, TEntiy> : IRepository<TEntiy>
        where TContext : EFRepositoryContext<TContext>
        where TEntiy : class, IDomainEntity, IAggregateRoot
    {
        protected virtual TContext DbContext { get; set; }

        protected Repository(TContext DbContext)
        {
            this.DbContext = DbContext;
        }

        public virtual IUnitOfWork UnitOfWork => DbContext;

        public virtual System.Linq.IQueryable<TEntiy> AsQueryable()
        {
            return DbContext.Set<TEntiy>().Where(e => e.IsDeleted == false);
        }

        public virtual TEntiy Add(TEntiy entity)
        {
            return DbContext.Add(entity).Entity;
        }

        public virtual Task<TEntiy> AddAsync(TEntiy entity, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(Add(entity));
        }

        public virtual TEntiy Update(TEntiy entity)
        {
            return DbContext.Update(entity).Entity;
        }

        public virtual Task<TEntiy> UpdateAsync(TEntiy entity, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(Update(entity));
        }

        public virtual bool Remove(TEntiy entity)
        {
            return DbContext.Remove(entity).Entity != null;
        }

        public virtual Task<bool> RemoveAsync(TEntiy entity, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(Remove(entity));
        }
    }

    public abstract class Repository<TContext, TEntiy, TKey> : Repository<TContext, TEntiy>, IRepository<TEntiy, TKey>
        where TContext : EFRepositoryContext<TContext>
        where TEntiy : class, IDomainEntity<TKey>, IAggregateRoot
        where TKey : notnull
    {
        protected Repository(TContext DbContext) : base(DbContext)
        {

        }

        public override IQueryable<TEntiy> AsQueryable()
        {
            return DbContext.Set<TEntiy>().Where(e => e.IsDeleted == false);
        }

        public virtual bool Remove(TKey Id)
        {
            var entity = DbContext.Find<TEntiy>(Id);
            if (entity == null)
            {
                return true;
            }
            return DbContext.Remove(entity).Entity != null;
        }

        public virtual Task<bool> RemoveAsync(TKey Id, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(Remove(Id));
        }

        public TEntiy Get(TKey Id)
        {
            var entity = DbContext.Find<TEntiy>(Id);
            if (entity == null)
            {
                throw new BusinessException("不存在该数据");
            }
            return entity;
        }

        public TEntiy Get(TKey Id, Guid RowVersion)
        {
            var entity = DbContext.Find<TEntiy>(Id);
            if (entity == null)
            {
                throw new BusinessException("不存在该数据");
            }
            if (entity.RowVersion != RowVersion)
            {
                throw new BusinessException("并发错误，数据已被改动");
            }
            return entity;
        }

        public TEntiy? GetOrNull(TKey Id)
        {
            var entity = DbContext.Find<TEntiy>(Id);
            return entity;
        }

        public TEntiy? GetOrNull(TKey Id, Guid RowVersion)
        {
            var entity = DbContext.Find<TEntiy>(Id);
            if (entity == null)
            {
                return entity;
            }
            if (entity.RowVersion != RowVersion)
            {
                throw new BusinessException("并发错误，数据已被改动");
            }
            return entity;
        }

        public virtual async Task<TEntiy> GetAsync(TKey Id, CancellationToken cancellationToken = default)
        {
            var entity = await DbContext.FindAsync<TEntiy>(Id);
            if (entity == null)
            {
                throw new BusinessException("不存在该数据") { Code = 404 };
            }
            return entity;
        }

        public virtual async Task<TEntiy> GetAsync(TKey Id, Guid RowVersion, CancellationToken cancellationToken = default)
        {
            var entity = await DbContext.FindAsync<TEntiy>(Id);
            if (entity == null)
            {
                throw new BusinessException("不存在该数据") { Code = 404 };
            }
            if (entity.RowVersion != RowVersion)
            {
                throw new BusinessException("并发错误，数据已被改动");
            }
            return entity;
        }

        public virtual async Task<TEntiy?> GetOrNullAsync(TKey Id, CancellationToken cancellationToken = default)
        {
            return await DbContext.FindAsync<TEntiy>(Id);
        }

        public virtual async Task<TEntiy?> GetOrNullAsync(TKey Id, Guid RowVersion, CancellationToken cancellationToken = default)
        {
            var entity = await DbContext.FindAsync<TEntiy>(Id);
            if (entity == null)
            {
                return entity;
            }
            if (entity.RowVersion != RowVersion)
            {
                throw new BusinessException("并发错误，数据已被改动");
            }
            return entity;
        }

        public virtual Task<TEntiy> MarkAsync(TEntiy entiy)
        {
            if (entiy.IsTransiented())
            {
                return AddAsync(entiy);
            }
            else
            {
                return UpdateAsync(entiy);
            }
        }
    }
}
